package com.xh.hongaicodegenerator.langgraph4j.node;

import cn.hutool.core.util.StrUtil;
import com.xh.hongaicodegenerator.core.AiCodeGeneratorFacade;
import com.xh.hongaicodegenerator.core.streamhandler.StreamHandlerExecutor;
import com.xh.hongaicodegenerator.langgraph4j.model.QualityResult;
import com.xh.hongaicodegenerator.langgraph4j.model.enums.TemplateTypeEnum;
import com.xh.hongaicodegenerator.langgraph4j.state.WorkflowContext;
import com.xh.hongaicodegenerator.model.entity.User;
import com.xh.hongaicodegenerator.model.enums.CodeGenTypeEnum;
import com.xh.hongaicodegenerator.monitor.FluxContext;
import com.xh.hongaicodegenerator.monitor.MonitorContextHolder;
import com.xh.hongaicodegenerator.service.ChatHistoryService;
import com.xh.hongaicodegenerator.utils.SpringContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.bsc.langgraph4j.action.AsyncNodeAction;
import org.bsc.langgraph4j.prebuilt.MessagesState;
import reactor.core.publisher.Flux;

import java.time.Duration;

import static org.bsc.langgraph4j.action.AsyncNodeAction.node_async;

/**
 * 网站代码生成节点
 */
@Slf4j
public class CodeGeneratorNode {

    private static final String CURLY_HINT = "--->检测出双‘{’插值语法，请在修改时使用v-text实现";
    public static AsyncNodeAction<MessagesState<String>> create() {
        return node_async(state -> {
            WorkflowContext context = WorkflowContext.getContext(state);
            log.info("执行节点: 代码生成");
            // 构造用户消息（包含原始提示词和可能的错误修复信息）
            FluxContext fluxContext = MonitorContextHolder.getFlux();
            String userMessage = buildUserMessage(context, fluxContext);
            CodeGenTypeEnum generationType = context.getGenerationType();
            TemplateTypeEnum templateType = context.getTemplateTypeEnum();
            fluxContext.getSink().next("\n\n生成代码开始\n");
            // 获取 AI 代码生成外观服务
            AiCodeGeneratorFacade codeGeneratorFacade = SpringContextUtil.getBean(AiCodeGeneratorFacade.class);
            log.info("开始生成代码，类型: {} ({})", generationType.getValue(), generationType.getText());
            // 获取 appId
            Long appId = context.getAppId();
            Flux<String> codeStream =null;
            if(isQualityCheckFailed(context.getQualityResult())){
                 codeStream = codeGeneratorFacade.fixErrorAndSaveCodeStream(userMessage, generationType, appId);
            }
            else {
                if (generationType != CodeGenTypeEnum.VUE_PROJECT || templateType==TemplateTypeEnum.WITHOUT_TEMPLATE){
                    codeStream = codeGeneratorFacade.createAndSaveCodeStream(userMessage, generationType, appId, false);
                }
                else{
                    codeStream = codeGeneratorFacade.createAndSaveCodeStream(userMessage, generationType, appId, true);
                }
            }
            User loginUser = context.getLoginUser();
            StreamHandlerExecutor handler=SpringContextUtil.getBean(StreamHandlerExecutor.class);
            Flux<String> processedCodeStream =handler.doExecute(codeStream, SpringContextUtil.getBean(ChatHistoryService.class), appId, loginUser, generationType, true);
            // 同步等待流式输出完成
            StringBuilder generatedCode = new StringBuilder();
            // 通过 doOnNext 转发数据到 fluxContext
            processedCodeStream
                    .doOnNext(data -> {
                        fluxContext.getSink().next(data);
                        generatedCode.append(data);
                    })  // 转发每个数据
                    .doOnError(err -> {
                        fluxContext.getSink().error(err);
                        String errorMessage = "AI回复失败: " + err.getMessage();
                        generatedCode.append(errorMessage);
                    }) // 转发错误
                    .doOnComplete(() -> {
                        // 保存ai的回复
                        fluxContext.setGeneratedCode(generatedCode.toString());
                    })
                    .blockLast(Duration.ofMinutes(20));  // 同步等待最多20分钟
            // 更新状态
            context.setCurrentStep("代码生成");
            Integer stepCount = context.getStepCount()+1;
            context.setStepCount(stepCount);
            fluxContext.getSink().next(String.format("\n\n第 %s 步 %s 执行完成\n\n",stepCount,"代码生成"));
            return WorkflowContext.saveContext(context);
        });
    }

    /**
     * 构造用户消息，如果存在质检失败结果则添加错误修复信息，第一次初始化增加初始化信息
     */
    private static String buildUserMessage(WorkflowContext context, FluxContext fluxContext) {
        String userMessage = context.getOriginalPrompt() + context.getImageListStr();
        // 检查是否存在质检失败结果
        QualityResult qualityResult = context.getQualityResult();
        if (isQualityCheckFailed(qualityResult)) {
            fluxContext.getSink().next("\n\n质量检查失败，正在重新生成\n\n");
            // 直接将错误修复信息作为新的提示词（起到了修改的作用）
            userMessage = buildErrorFixPrompt(qualityResult);
        }else{
            Boolean initiatedCode = context.getInitiatedCode();
            //第一次chat时才进行读取代码
            if (!isQualityCheckFailed(qualityResult)&&initiatedCode){
                userMessage = buildInitiatedCodePrompt(initiatedCode, userMessage);
            }
        }
        return userMessage;
    }

    /**
     * 判断质检是否失败
     */
    private static boolean isQualityCheckFailed(QualityResult qualityResult) {
        return qualityResult != null &&
                !qualityResult.getIsValid() &&
                qualityResult.getErrors() != null &&
                !qualityResult.getErrors().isEmpty();
    }

    /**
     * 构造错误修复提示词
     */
    private static String buildErrorFixPrompt(QualityResult qualityResult) {
        StringBuilder errorInfo = new StringBuilder();
        errorInfo.append("\n\n## 上次生成的代码存在以下问题，请修复：\n");
        // 添加错误列表
        qualityResult.getErrors().forEach(error ->
                errorInfo.append("- ").append(escapeCurlyWithHint(error)).append("\n"));
        // 添加修复建议（如果有）
        if (qualityResult.getSuggestions() != null && !qualityResult.getSuggestions().isEmpty()) {
            errorInfo.append("\n## 修复建议：\n");
            qualityResult.getSuggestions().forEach(suggestion ->
                    errorInfo.append("- ").append(escapeCurlyWithHint(suggestion)).append("\n"));
        }
        errorInfo.append("\n请根据上述问题和建议修改相关代码，确保修复所有提到的问题。");
        return errorInfo.toString();
    }

    /**
     * 构造初始化代码提示词
     */
    public static String buildInitiatedCodePrompt(Boolean initatedCode, String userMessage) {
        if (initatedCode) return "\n我已经在当前目录下初始化了一个可运行的、与需求相关的项目，请读取有关代码，对初始代码进行修改以满足我下面的需求：\n"+userMessage;
        return userMessage;
    }

    /**
     * 将 Vue 模板中的 {{xxx}} 替换为 [[open]]xxx[[close]]，避免prompt解析错误
     * 只处理双大括号插值
     */
    public static String escapeCurlyWithHint(String template) {
        if (template == null || template.isEmpty()) return template;

        StringBuilder sb = new StringBuilder();
        int i = 0;
        int n = template.length();

        while (i < n) {
            int startIdx = template.indexOf("{{", i);
            if (startIdx == -1) {
                // 剩余文本直接添加
                sb.append(template.substring(i));
                break;
            }

            // 添加 '{{' 前面的普通文本
            sb.append(template, i, startIdx);

            int endIdx = template.indexOf("}}", startIdx + 2);
            if (endIdx == -1) {
                // 没有匹配的 '}}'，把剩余部分直接添加
                sb.append(template.substring(startIdx));
                break;
            }

            // 提取变量名并去掉空格
            String varName = template.substring(startIdx + 2, endIdx).trim();

            // 替换为 [[open]]xxx[[close]]
            sb.append("[[open]]").append(varName).append("[[close]]");

            i = endIdx + 2; // 跳过 '}}'
        }

        return sb.toString();
    }

    /**
     * 反转义函数，将 [[open]]xxx[[close]] 替换回 {{xxx}}
     *
     * @param content 包含转义内容的字符串
     * @return 反转义后的内容
     */
    public static String unescapeCurlyBrackets(String content) {
        if (content == null || content.isEmpty()) return content;

        StringBuilder sb = new StringBuilder();
        int i = 0;
        int n = content.length();

        while (i < n) {
            int startIdx = content.indexOf("[[open]]", i);
            if (startIdx == -1) {
                // 剩余文本直接添加
                sb.append(content.substring(i));
                break;
            }

            // 添加 "[[open]]" 前面的普通文本
            sb.append(content, i, startIdx);

            int endIdx = content.indexOf("[[close]]", startIdx + 8);
            if (endIdx == -1) {
                // 没有匹配的 "[[close]]"，把剩余部分直接添加
                sb.append(content.substring(startIdx));
                break;
            }

            // 提取变量名
            String varName = content.substring(startIdx + 8, endIdx);

            // 替换为 {{xxx}}
            sb.append("{{").append(varName).append("}}");

            i = endIdx + 9; // 跳过 "[[close]]"
        }

        return sb.toString();
    }



}